
using System;
using System.Collections.Generic;
using ScrewTurn.Wiki.PluginFramework;
using System.Text;

namespace ScrewTurn.Wiki {

	/// <summary>
	/// Implements the <b>IHost</b> interface.
	/// </summary>
	[Serializable]
	public class Host : IHost {

		private static Host instance;

		/// <summary>
		/// Gets or sets the singleton instance of the <b>Host</b> object.
		/// </summary>
		public static Host Instance {
			get {
				if(instance == null) throw new InvalidOperationException("Host.Instance is null");
				return instance;
			}
			set { instance = value; }
		}

		private Dictionary<string, CustomToolbarItem> customSpecialTags;

		/// <summary>
		/// Initializes a new instance of the <b>PluginHost</b> class.
		/// </summary>
		public Host() {
			customSpecialTags = new Dictionary<string, CustomToolbarItem>(5);
		}

		/// <summary>
		/// Gets the Special Tags added by providers.
		/// </summary>
		public Dictionary<string, CustomToolbarItem> CustomSpecialTags {
			get {
				lock(customSpecialTags) {
					return customSpecialTags;
				}
			}
		}

		/// <summary>
		/// Gets the values of the Wiki Settings.
		/// </summary>
		/// <param name="name">The Setting's Name.</param>
		/// <returns>The Setting's value.</returns>
		public string GetSettingValue(SettingName name) {
			switch(name) {
				case SettingName.AccessStatus:
					if(Settings.PublicAccess) return "PUBLIC";
					else if(Settings.PrivateAccess) return "PRIVATE";
					else return "NORMAL";
				case SettingName.ContactEmail:
					return Settings.ContactEmail;
				case SettingName.DateTimeFormat:
					return Settings.DateTimeFormat;
				case SettingName.DefaultLanguage:
					return Settings.DefaultLanguage;
				case SettingName.DefaultPage:
					return Settings.DefaultPage;
				case SettingName.DefaultTimeZone:
					return Settings.DefaultTimezone;
				case SettingName.MainUrl:
					return Settings.MainUrl;
				case SettingName.SenderEmail:
					return Settings.SenderEmail;
				case SettingName.Theme:
					return Settings.Theme;
				case SettingName.ThemePath:
					return Settings.ThemePath;
				case SettingName.WikiTitle:
					return Settings.WikiTitle;
				case SettingName.ThemesDirectory:
					return Settings.ThemesDirectory;
				case SettingName.PublicDirectory:
					return Settings.PublicDirectory;
				case SettingName.MessagesDirectory:
					return Settings.MessagesDirectory;
				case SettingName.PagesDirectory:
					return Settings.PagesDirectory;
				case SettingName.PluginsDirectory:
					return Settings.PluginsDirectory;
				case SettingName.SnippetsDirectory:
					return Settings.SnippetsDirectory;
				case SettingName.TempDirectory:
					return Settings.TempDirectory;
				case SettingName.UploadDirectory:
					return Settings.UploadDirectory;
				case SettingName.UsersCanCreateNewPages:
					return Settings.UsersCanCreateNewPages.ToString();
				case SettingName.UsersCanCreateNewCategories:
					return Settings.UsersCanCreateNewCategories.ToString();
				case SettingName.UsersCanDeleteFiles:
					return Settings.UsersCanDeleteFiles.ToString();
				case SettingName.UsersCanManageCategories:
					return Settings.UsersCanManagePageCategories.ToString();
				case SettingName.UsersCanRegister:
					return Settings.UsersCanRegister.ToString();
				case SettingName.UsersCanUploadFiles:
					return Settings.UsersCanUploadFiles.ToString();
				case SettingName.UsersCanViewFiles:
					return Settings.UsersCanViewFiles.ToString();
				case SettingName.UsernameRegex:
					return Settings.UsernameRegex;
				case SettingName.PasswordRegex:
					return Settings.PasswordRegex;
				case SettingName.EmailRegex:
					return Settings.EmailRegex;
				case SettingName.MainUrlRegex:
					return Settings.MainUrlRegex;
				case SettingName.EnableDoubleClickEditing:
					return Settings.EnableDoubleClickEditing.ToString();
				case SettingName.ProcessSingleLineBreaks:
					return Settings.ProcessSingleLineBreaks.ToString();
				case SettingName.AccountActivationMode:
					return Settings.AccountActivationMode;
				case SettingName.AllowedFileTypes:
					StringBuilder sb = new StringBuilder(50);
					foreach(string s in Settings.AllowedFileTypes) sb.Append(s + ",");
					return sb.ToString().TrimEnd(',');
				case SettingName.ConfigVisibleToAdmins:
					return Settings.ConfigVisibleToAdmins.ToString();
				case SettingName.DisableAutomaticVersionCheck:
					return Settings.DisableAutomaticVersionCheck.ToString();
				case SettingName.DisableBreadcrumbsTrail:
					return Settings.DisableBreadcrumbsTrail.ToString();
				case SettingName.DisableCache:
					return Settings.DisableCache.ToString();
				case SettingName.DisableCaptchaControl:
					return Settings.DisableCaptchaControl.ToString();
				case SettingName.DisableConcurrentEditing:
					return Settings.DisableConcurrentEditing.ToString();
				case SettingName.DiscussionPermissions:
					return Settings.DiscussionPermissions;
				case SettingName.EnableHttpCompression:
					return Settings.EnableHttpCompression.ToString();
				case SettingName.EnableViewStateCompression:
					return Settings.EnableViewStateCompression.ToString();
				case SettingName.FileAccessTries:
					return Settings.FileAccessTries.ToString();
				case SettingName.FileAccessTryDelay:
					return Settings.FileAccessTryDelay.ToString();
				case SettingName.FileManagementInPublicAccessAllowed:
					return Settings.FileManagementInPublicAccessAllowed.ToString();
				case SettingName.LoggingLevel:
					return Settings.LoggingLevel.ToString();
				case SettingName.MaxFileSize:
					return Settings.MaxFileSize.ToString();
				case SettingName.PageExtension:
					return Settings.PageExtension;
				case SettingName.ScriptTagsAllowed:
					return Settings.ScriptTagsAllowed.ToString();
				case SettingName.WikiVersion:
					return Settings.WikiVersion;
			}
			return "";
		}

		/// <summary>
		/// Gets the list of the Users.
		/// </summary>
		public UserInfo[] AllUsers {
			get { return Users.Instance.AllUsers.ToArray(); }
		}

		/// <summary>
		/// Finds a user by username, properly handling Users Storage Providers.
		/// </summary>
		/// <param name="username">The username.</param>
		/// <returns>The <see cref="T:UserInfo" />, or <b>null</b> if no users are found.</returns>
		public UserInfo FindUser(string username) {
			return Users.Instance.Find(username, true);
		}

		/// <summary>
		/// Gets the list of the Wiki Pages.
		/// </summary>
		public PageInfo[] AllPages {
			get { return Pages.Instance.AllPages.ToArray(); }
		}

		/// <summary>
		/// Gets the List of Categories.
		/// </summary>
		public CategoryInfo[] AllCategories {
			get { return Pages.Instance.AllCategories.ToArray(); }
		}

		/// <summary>
		/// Gets the list of Snippets.
		/// </summary>
		public Snippet[] AllSnippets {
			get { return Snippets.Instance.AllSnippets.ToArray(); }
		}

		/// <summary>
		/// Gets the list of Navigation Paths.
		/// </summary>
		public NavigationPath[] AllNavigationPaths {
			get { return NavigationPaths.Instance.Paths.ToArray(); }
		}

		/// <summary>
		/// Gets the Categories of a Page.
		/// </summary>
		/// <param name="page">The Page.</param>
		/// <returns>The Categories.</returns>
		public CategoryInfo[] GetCategoriesPerPage(PageInfo page) {
			return Pages.Instance.GetCategoriesPerPage(page);
		}

		/// <summary>
		/// Gets a Wiki Page.
		/// </summary>
		/// <param name="name">The Name of the Page.</param>
		/// <returns>The Wiki Page.</returns>
		public PageInfo FindPage(string name) {
			return Pages.Instance.FindPage(name);
		}

		/// <summary>
		/// Gets the Content of a Page.
		/// </summary>
		/// <param name="page">The Page.</param>
		/// <returns>The Page Content.</returns>
		public PageContent GetPageContent(PageInfo page) {
			return Content.GetPageContent(page, true);
		}

		/// <summary>
		/// Gets the Backup/Revision numbers of a Page.
		/// </summary>
		/// <param name="page">The Page.</param>
		/// <returns>The Backup/Revision numbers.</returns>
		public int[] GetBackups(PageInfo page) {
			return Pages.Instance.GetBackups(page).ToArray();
		}

		/// <summary>
		/// Gets the Content of a Page Backup.
		/// </summary>
		/// <param name="page">The Page.</param>
		/// <param name="revision">The revision.</param>
		/// <returns>The Backup Content.</returns>
		public PageContent GetBackupContent(PageInfo page, int revision) {
			return Pages.Instance.GetBackupContent(page, revision);
		}

		/// <summary>
		/// Gets the formatted content of a Wiki Page.
		/// </summary>
		/// <param name="page">The Page.</param>
		/// <returns>The formatted content.</returns>
		public string GetFormattedContent(PageInfo page) {
			PageInfo pageInfo = Pages.Instance.FindPage(page.Name);
			if(pageInfo == null) return null;
			PageContent content = Content.GetPageContent(pageInfo, true);
			return Formatter.Format(content.Content, page);
		}

		/// <summary>
		/// Formats a block of WikiMarkup, using the built-in formatter only.
		/// </summary>
		/// <param name="raw">The block of WikiMarkup.</param>
		/// <returns>The formatted content.</returns>
		public string Format(string raw) {
			return Formatter.Format(raw, null);
		}

		/// <summary>
		/// Sends an Email.
		/// </summary>
		/// <param name="recipient">The Recipient Email address.</param>
		/// <param name="sender">The Sender's Email address.</param>
		/// <param name="subject">The Subject.</param>
		/// <param name="body">The Body.</param>
		/// <param name="html">True if the message is HTML.</param>
		/// <returns>True if the message has been sent successfully.</returns>
		public bool SendEmail(string recipient, string sender, string subject, string body, bool html) {
			try {
				EmailSender.Send(recipient, sender, subject, body, html);
				return true;
			}
			catch {
				return false;
			}
		}

		/// <summary>
		/// Logs a new message.
		/// </summary>
		/// <param name="message">The Message.</param>
		/// <param name="entryType">The Entry Type.</param>
		/// <param name="caller">The caller Plugin.</param>
		public void LogEntry(string message, LogEntryType entryType, object caller) {
			if(caller == null) throw new ArgumentNullException("The Caller cannot be null.");

			EntryType t = EntryType.General;
			switch(entryType) {
				case LogEntryType.General:
					t = EntryType.General;
					break;
				case LogEntryType.Warning:
					t = EntryType.Warning;
					break;
				case LogEntryType.Error:
					t = EntryType.Error;
					break;
			}
			string name = "?";
			if(caller is IUsersStorageProvider) name = ((IUsersStorageProvider)caller).Information.Name;
			else if(caller is IPagesStorageProvider) name = ((IPagesStorageProvider)caller).Information.Name;
			else if(caller is IFormatterProvider) name = ((IFormatterProvider)caller).Information.Name;
			Log.LogEntry(message, t, name + "+SYSTEM");
		}

		/// <summary>
		/// Reads a text file.
		/// </summary>
		/// <param name="filename">The filename.</param>
		/// <returns>The content of the file.</returns>
		public string ReadFile(string filename) {
			string r = null;
			try {
				r = Tools.LoadFile(filename);
			}
			catch { }
			return r;
		}

		/// <summary>
		/// Writes a text file.
		/// </summary>
		/// <param name="filename">The filename.</param>
		/// <param name="content">The content.</param>
		/// <returns>True if the file is written.</returns>
		public bool WriteFile(string filename, string content) {
			bool done = false;
			try {
				Tools.WriteFile(filename, content);
				done = true;
			}
			catch { }
			return done;
		}

		/// <summary>
		/// Aligns a Date and Time object to the User's Time Zone preferences.
		/// </summary>
		/// <param name="dt">The Date/Time to align.</param>
		/// <returns>The aligned Date/Time.</returns>
		/// <remarks>The method takes care of daylight saving settings.</remarks>
		public DateTime AlignDateTimeWithPreferences(DateTime dt) {
			return Tools.AlignWithPreferences(dt, Settings.DefaultTimezone);
		}

		/// <summary>
		/// Forces to reload a list of items.
		/// </summary>
		/// <param name="list">The list to Reload.</param>
		/// <param name="caller">The Component that calls the method. The caller cannot be null.</param>
		public void RequestRefresh(RefreshList list, object caller) {
			if(caller == null) throw new ArgumentNullException("The Caller cannot be null.");

			if(caller is IUsersStorageProvider && list == RefreshList.Users) {
				Users.Instance.ReloadFrom(caller as IUsersStorageProvider);
			}
			else if(caller is IPagesStorageProvider && list == RefreshList.Pages) {
				Pages.Instance.ReloadFrom(caller as IPagesStorageProvider);
			}
			else throw new ArgumentException("Invalid Caller object or List.");
		}

		/// <summary>
		/// Clears the cache.
		/// </summary>
		/// <param name="data">The part of the cache to clear.</param>
		public void ClearCache(CacheData data) {
			switch(data) {
				case CacheData.Pages:
					Content.InvalidateAll();
					break;
				case CacheData.MetaFiles:
					Content.PseudoCache.Clear();
					break;
				default:
					throw new ArgumentException("Invalid CacheData");
			}
		}

		/// <summary>
		/// Adds an item in the Editing Toolbar.
		/// </summary>
		/// <param name="item">The item to add.</param>
		/// <param name="text">The text of the item.</param>
		/// <param name="value">The value of the item.</param>
		public void AddToolbarItem(ToolbarItem item, string text, string value) {
			if(string.IsNullOrEmpty(text)) throw new ArgumentNullException("text", "Parameter text cannot be empty");
			if(string.IsNullOrEmpty(value)) throw new ArgumentNullException("value", "Parameter value cannot be empty");
			if(value.Contains("\"") || value.Contains("'")) throw new ArgumentException("Parameter value cannot contain single or double quotes", "value");

			if(item == ToolbarItem.SpecialTagWrap && !value.Contains("|")) throw new ArgumentException("value", "Invalid value for a SpecialTagWrap");

			lock(customSpecialTags) {
				if(customSpecialTags.ContainsKey(text)) {
					customSpecialTags[text].Value = value;
					customSpecialTags[text].Item = item;
				}
				else customSpecialTags.Add(text, new CustomToolbarItem(item, text, value));
			}
		}

		/// <summary>
		/// Event fired whenever an activity is performed on a User Account.
		/// </summary>
		public event EventHandler<UserAccountActivityEventArgs> UserAccountActivity;

		/// <summary>
		/// Fires the UserAccountActivity event.
		/// </summary>
		/// <param name="user">The user the activity refers to.</param>
		/// <param name="activity">The activity.</param>
		public void OnUserAccountActivity(UserInfo user, UserAccountActivity activity) {
			if(UserAccountActivity != null) {
				UserAccountActivity(this, new UserAccountActivityEventArgs(user, activity));
			}
		}

		/// <summary>
		/// Even fired whenever an activity is performed on a Page.
		/// </summary>
		public event EventHandler<PageActivityEventArgs> PageActivity;

		/// <summary>
		/// Fires the PageActivity event.
		/// </summary>
		/// <param name="page">The page the activity refers to.</param>
		/// <param name="author">The author of the activity.</param>
		/// <param name="activity">The activity.</param>
		public void OnPageActivity(PageInfo page, string author, PageActivity activity) {
			if(PageActivity != null) {
				PageActivity(this, new PageActivityEventArgs(page, author, activity));
			}
		}


	}

	/// <summary>
	/// Represents a custom toolbar item.
	/// </summary>
	[Serializable]
	public class CustomToolbarItem {

		private ToolbarItem item;
		private string text, value;

		/// <summary>
		/// Initializes a new instance of the <b>ToolbarItem</b> class.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <param name="text">The text.</param>
		/// <param name="value">The value.</param>
		public CustomToolbarItem(ToolbarItem item, string text, string value) {
			this.item = item;
			this.text = text;
			this.value = value;
		}

		/// <summary>
		/// Gets or sets the item.
		/// </summary>
		public ToolbarItem Item {
			get { return item; }
			set { item = value; }
		}

		/// <summary>
		/// Gets the text.
		/// </summary>
		public string Text {
			get { return text; }
		}

		/// <summary>
		/// Gets or sets the value.
		/// </summary>
		public string Value {
			get { return value; }
			set { this.value = value; }
		}

	}

}
